package com.uuah.server.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.List;

import com.uuah.server.bootstrap.wrapper.DefaultUuahClassPathConfig;
import com.uuah.server.bootstrap.wrapper.UuahBootstrap;

public final class UuahBootstrapUtils {
	private static final String UUAH_LIB_FILENAME = "lib" + File.separator
			+ "uuah";
	private static final String UUAH_HOME = System.getProperty("uuahAppServer.home");

	private UuahBootstrapUtils() {
		// utility class only
	}

	public static File getUuahHomeFile() {
		return new File(UUAH_HOME);
	}

	public static File getUuahLibDir() {
		return new File(UUAH_HOME + File.separator + UUAH_LIB_FILENAME);
	}


	public static void addLocalJarFilesToClasspath(File uuahHome, File uuahBase)
			throws Exception {
		DefaultUuahClassPathConfig classPath = new DefaultUuahClassPathConfig(
				uuahHome, uuahBase);
		addLibrariesToClasspath(classPath.getURLs());
	}

	public static void addLibrariesToClasspath(List urls)
			throws NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
		ClassLoader sys = ClassLoader.getSystemClassLoader();
		if (!(sys instanceof URLClassLoader)) {
			throw new IllegalArgumentException(
					"PANIC: Uuah has been started with an unsupported classloader: "
							+ sys.getClass().getName()
							+ ". "
							+ "Please report this error to user<at>developer<dot>uuah<dot>com");
		}
		URLClassLoader sysCl = (URLClassLoader) sys;

		Class refClass = URLClassLoader.class;
		Method methodAddUrl = refClass.getDeclaredMethod("addURL",
				new Class[] { URL.class });
		methodAddUrl.setAccessible(true);
		for (Iterator it = urls.iterator(); it.hasNext();) {
			URL url = (URL) it.next();
			methodAddUrl.invoke(sysCl, new Object[] { url });
		}
	}

	public static class ProxyInfo {
		String host;
		String port;
		String username;
		String password;

		public ProxyInfo(String host, String port) {
			this(host, port, null, null);
		}

		public ProxyInfo(String host, String port, String username,
				String password) {
			this.host = host;
			this.port = port;
			this.username = username;
			this.password = password;
		}
	}

	public static URL getResource(final String resourceName,
			final Class callingClass) {
		URL url = (URL) AccessController.doPrivileged(new PrivilegedAction() {
			public Object run() {
				final ClassLoader cl = Thread.currentThread()
						.getContextClassLoader();
				return cl != null ? cl.getResource(resourceName) : null;
			}
		});

		if (url == null) {
			url = (URL) AccessController.doPrivileged(new PrivilegedAction() {
				public Object run() {
					return UuahBootstrap.class.getClassLoader().getResource(
							resourceName);
				}
			});
		}

		if (url == null) {
			url = (URL) AccessController.doPrivileged(new PrivilegedAction() {
				public Object run() {
					return callingClass.getClassLoader().getResource(
							resourceName);
				}
			});
		}

		return url;
	}

	/**
	 * @see FileUtils.renameFile()
	 */
	public static boolean renameFile(File srcFile, File destFile)
			throws IOException {
		boolean isRenamed = false;
		if (srcFile != null && destFile != null) {
			if (!destFile.exists()) {
				if (srcFile.isFile()) {
					isRenamed = srcFile.renameTo(destFile);
					if (!isRenamed && srcFile.exists()) {
						isRenamed = renameFileHard(srcFile, destFile);
					}
				}
			}
		}
		return isRenamed;
	}

	/**
	 * @see FileUtils.renameFileHard()
	 */
	public static boolean renameFileHard(File srcFile, File destFile)
			throws IOException {
		boolean isRenamed = false;
		if (srcFile != null && destFile != null) {
			if (!destFile.exists()) {
				if (srcFile.isFile()) {
					FileInputStream in = null;
					FileOutputStream out = null;
					try {
						in = new FileInputStream(srcFile);
						out = new FileOutputStream(destFile);
						out.getChannel().transferFrom(in.getChannel(), 0,
								srcFile.length());
						isRenamed = true;
					} finally {
						if (in != null) {
							in.close();
						}
						if (out != null) {
							out.close();
						}
					}
					if (isRenamed) {
						srcFile.delete();
					} else {
						destFile.delete();
					}
				}
			}
		}
		return isRenamed;
	}

	/**
	 * @see IOUtils.copy()
	 */
	public static int copy(InputStream input, OutputStream output)
			throws IOException {
		long count = copyLarge(input, output);
		if (count > Integer.MAX_VALUE) {
			return -1;
		}
		return (int) count;
	}

	/**
	 * @see IOUtils.copyLarge()
	 */
	public static long copyLarge(InputStream input, OutputStream output)
			throws IOException {
		byte[] buffer = new byte[1024 * 4];
		long count = 0;
		int n = 0;
		while (-1 != (n = input.read(buffer))) {
			output.write(buffer, 0, n);
			count += n;
		}
		return count;
	}
}
